/*
 * Copyright 2011 Kim Lindhardt Madsen
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package dk.lindhardt.gwt.geie.server;

import dk.lindhardt.gwt.geie.client.*;
import dk.lindhardt.gwt.geie.shared.*;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * User: AnAmuser
 * Date: 15-06-11
 * <p/>
 * Class for building csv from a {@link dk.lindhardt.gwt.geie.shared.TableLayout}
 */
public class TableLayout2CSV {

   private Logger logger = Logger.getLogger("TableLayout2CSV");

   private TableLayout layout;
   private String separator = ",";
   private String charset = "utf-8";
   private boolean header = true;
   private final String NEWLINE = System.getProperty("line.separator");
   private final String QUOTE = "\"";

   /**
    * New instance of csv builder. Uses default separator <code>","</code>, charset <code>UTF-8</code>
    * and header <code>true</code>
    * @param layout the table layout to build csv from
    */
   public TableLayout2CSV(TableLayout layout) {
      this.layout = layout;
   }

   /**
    * Writes csv to the output stream.
    * @param out the output stream
    */
   public void build(OutputStream out) {
      int columns = layout.columns();
      int rows = layout.rows();

      Set<Cell> processedCells = new HashSet<Cell>();
      try {
         for (int r = 0; r < rows; r++) {
            String[] row = new String[columns];
            for (int c = 0; c < columns; c++) {
               Cell cell = layout.getCell(r, c);
               if (cell != null && !processedCells.contains(cell)) {
                  CellFormat cellFormat = layout.getCellFormat(cell.getCellFormatKey());
                  if (cell instanceof DateCell) {
                     if (cellFormat.getPattern() != null && cellFormat.getPattern().length() > 0) {
                        DateFormat format = new SimpleDateFormat(cellFormat.getPattern());
                        row[c] = format.format(((DateCell) cell).getDateValue());
                     }
                  } else if (cell instanceof NumberCell) {
                     if (cellFormat.getPattern() != null && cellFormat.getPattern().length() > 0) {
                        NumberFormat format = new DecimalFormat(cellFormat.getPattern());
                        row[c] = format.format(cell.getValue());
                     }
                  } else {
                     row[c] = cell.getValue().toString();
                  }
                  processedCells.add(cell);
               } else {
                  row[c] = "";
               }
            }
            if (r == 0 && header) {
               exportHeader(out, Arrays.asList(row));
            } else {
               writeRow(out, Arrays.asList(row));
            }
         }
      } catch (Exception e) {
         logger.log(Level.SEVERE, "Error writing csv to output stream", e);
      }
   }

   /**
    * Sets the charset to be used (default is utf-8)
    * @param charset the charset
    */
   public void setCharset(String charset) {
      this.charset = charset;
   }

   /**
    * Sets the seperator to be used between cells
    * @param separator the comma
    */
   public void setSeparator(String separator) {
      this.separator = separator;
   }

   /**
    * Sets if it should include a header. The header is not escaped like the following rows.
    * (Defaults to true)
    * @param header include header
    */
   public void setHeader(boolean header) {
      this.header = header;
   }

   /**
    * Write header to OutputStream
    */
   public void exportHeader(OutputStream outputStream, List<String> header) throws IOException {
      writeRow(outputStream, header);
   }

   /**
    * Writes row to OutputStream
    */
   private void writeRow(OutputStream outputStream, List<String> row) throws IOException {
      int columns = row.size();
      for (int index = 0; index < columns; index++) {
         String data = row.get(index);
         if (data.contains(separator) || data.contains(QUOTE)) {
            write(outputStream, escape(data));
         } else {
            write(outputStream, data);
         }
         if (index < columns - 1) {
            write(outputStream, separator);
         } else {
            write(outputStream, NEWLINE);
         }
      }
   }

   /**
    * Writes string to OutputStream
    */
   private void write(OutputStream outputStream, String data) throws IOException {
      outputStream.write(data.getBytes(Charset.forName(charset)));
   }

   /**
    * Puts string between quote
    */
   private String escape(String data) {
      return QUOTE + data + QUOTE;
   }

}
